﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;

namespace SQLTouch.Client.Metro.Cache
{
    public class RoamingCache<T> : ICache<T>
    {
        private readonly string containerName;
        private readonly Func<T, string> keySelector;

        public event Action<T> ItemAdded;
        public event Action<T> ItemUpdated;
        public event Action<T> ItemDeleted;

        public RoamingCache(string containerName, Func<T, string> keySelector)
        {
            this.containerName = containerName;
            this.keySelector = keySelector;
        }

        public string ContainerName
        {
            get { return containerName; }
        } 

        private static string Serialize(T obj)
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof(T));
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (StreamReader reader = new StreamReader(memoryStream))
                {
                    serializer.WriteObject(memoryStream, obj);
                    memoryStream.Position = 0;
                    return reader.ReadToEnd();
                }
            }
        }

        private static T Deserialize(string xml)
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof(T));
            using (Stream stream = new MemoryStream())
            {
                byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
                stream.Write(data, 0, data.Length);
                stream.Position = 0;
                return (T)serializer.ReadObject(stream);
            }
        }

        private ApplicationDataContainer GetContainer()
        {
            ApplicationDataContainer roamingSettings = ApplicationData.Current.RoamingSettings;
            ApplicationDataContainer container = roamingSettings.CreateContainer(containerName, ApplicationDataCreateDisposition.Always);
            return container;
        }

        public IEnumerable<T> GetItems()
        {
            foreach (var value in GetContainer().Values)
            {
                T model;
                try
                {
                    string xml = (string)value.Value;
                    model = Deserialize(xml);
                }
                catch
                {
                    continue;
                }
                yield return model;
            }
        }

        public void Add(T item)
        {
            GetContainer().Values.Add(keySelector(item), Serialize(item));
            RaiseItemAdded(item);
        }

        public void Delete(T item)
        {
            if (GetContainer().Values.Remove(keySelector(item)))
            {
                RaiseItemDeleted(item);
            }
        }

        public void Update(T item)
        {
            GetContainer().Values[keySelector(item)] = Serialize(item);
            RaiseItemUpdated(item);
        }

        private void RaiseItemAdded(T item)
        {
            var handler = ItemAdded;
            if (handler != null)
            {
                handler(item);
            }
        }

        private void RaiseItemDeleted(T item)
        {
            var handler = ItemDeleted;
            if (handler != null)
            {
                handler(item);
            }
        }

        private void RaiseItemUpdated(T item)
        {
            var handler = ItemUpdated;
            if (handler != null)
            {
                handler(item);
            }
        }
    }
}
